/**
 * Copyright 2011 Clemens Lombriser <clemens@lom.ch>
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * 
 */

package ch.lom.clemens.android.bibliography.data;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;

import oauth.signpost.OAuthConsumer;
import oauth.signpost.OAuthProvider;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.basic.DefaultOAuthProvider;
import oauth.signpost.signature.SignatureMethod;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;
import ch.lom.clemens.android.bibliography.OAuthKeys;
import ch.lom.clemens.android.bibliography.StartActivity;

public class JSONWebConnector {

	public JSONWebConnector() {
        m_provider = new DefaultOAuthProvider(m_consumer,
        		"http://www.mendeley.com/oauth/request_token/",
		        "http://www.mendeley.com/oauth/access_token/",
		        "http://www.mendeley.com/oauth/authorize/");
	}

	// ////////////////////////////////////////////////////////////////////////
	// Authentication
	
	public boolean isConnected() {
		return m_state == STATE_CONNECTED;
	}
	
	public String getAuthenticationURL() throws Exception {
		
		if (m_state != STATE_CONNECTING) {
			m_state = STATE_CONNECTING;
			m_authURL = m_provider.retrieveRequestToken(CALLBACK_REROUTE);
			return m_authURL;
		} else {
			return null;			
		}
	}
    
	public boolean setVerificationCode(String code) {
		try {
			m_provider.retrieveAccessToken(code);
		} catch (Exception e) {
			m_state = STATE_OFFLINE;
			return false;
		}
		m_state = STATE_CONNECTED;

		return true;
	}
	
	public static final String CALLBACK = "oauthmendeley://mendeley";
	//public static final String CALLBACK2 = "OauthMendeley%3A%2F%2Fmendeley";
	public static final String CALLBACK_REROUTE = "http://clemens.lom.ch/oauth.php";
	
	// ////////////////////////////////////////////////////////////////////////
	// Access to the data

	public MCollection [] getCollections() {
		
		if ( !isConnected()) {
			StartActivity.singleton.doAuthentication(StartActivity.MENDELEY_RETRIEVE_COLLECTIONS);
			return null;
		}
		
		
		MCollection [] collections = null;
		
		try {
			String strResponse = connect("http://www.mendeley.com/oapi/library/collections?consumer_key=" + m_consumerkey);

			Log.i("MendeleyComm", strResponse);

			JSONArray jcols = new JSONArray(strResponse);
			
			collections = new MCollection[jcols.length()];
		
			for (int i=0; i< jcols.length(); i++) {
				JSONObject collection = jcols.getJSONObject(i);
				
				collections[i] = new MCollection(null);
				
				collections[i].id   = collection.getString("id");
				collections[i].name = collection.getString("name");
				collections[i].type = collection.getString("type");
				collections[i].size = collection.getInt("size");
			}
		} catch (Exception e) {
			Log.e("MendeleyConnector","Got exception when parsing online collection data");
			Log.e("MendeleyConnector",e.getClass().getSimpleName() + ": " + e.getMessage());
		}
		return collections;

	}
	
	public String [] getCollectionDocIDs(MCollection col) {
		return getCollectionDocIDs(col, 0);
	}
	
	public String [] getCollectionDocIDs(MCollection col, int page) {
		if (! isConnected()) {
			StartActivity.singleton.doAuthentication(StartActivity.MENDELEY_RETRIEVE_COLLECTION);
			return null;
		}
		
		String [] ids = null;
		try {
			String strResponse;
			if (col.m_coltype == MCollection.AUTHORED) {
				strResponse = connect("http://www.mendeley.com/oapi/library/documents/authored/?page="+page+"&consumer_key=" + m_consumerkey);
			} else if (col.m_coltype == MCollection.LIBRARY) {
				strResponse = connect("http://www.mendeley.com/oapi/library/?page="+page+"&consumer_key=" + m_consumerkey);
			} else {
				strResponse = connect("http://www.mendeley.com/oapi/library/collections/"+col.id+"/?page="+page+"&consumer_key=" + m_consumerkey);
			}

			Log.i("MendeleyComm", "Collection: " + col.id + "\n" +strResponse);
			
			JSONObject colData = new JSONObject(strResponse);

			JSONArray documents = colData.getJSONArray("document_ids");
			ids = new String[documents.length()];
			for (int i=0; i< documents.length(); i++) {
				ids[i] = documents.getString(i);
			}
			
			// make sure we get all pages
			int totalpages = colData.getInt("total_pages");
			if (page+1 < totalpages ) {
				String [] adddocs = getCollectionDocIDs(col, page+1);
				
				String [] alldocs = new String[ids.length + adddocs.length];
				for (int i=0;i<ids.length;i++) alldocs[i] = ids[i];
				for (int i=0;i<adddocs.length;i++) alldocs[ids.length+i] = adddocs[i];
				ids = alldocs;
			}
			
		} catch (Exception e) {
			Log.e("MendeleyConnector","Got a " + e.getClass().getName() + ": " + e.getMessage());
		}
		
		return ids;
	}
	
	public MGroup [] getGroups(MendeleyConnector con) {
		
		if ( !isConnected()) {
			StartActivity.singleton.doAuthentication(StartActivity.MENDELEY_RETRIEVE_COLLECTIONS);
			return null;
		}
		
		
		MGroup [] groups = null;
		
		try {
			String strResponse = connect("http://www.mendeley.com/oapi/library/groups?consumer_key=" + m_consumerkey);

			Log.i("MendeleyComm", strResponse);

			JSONArray items = new JSONArray(strResponse);
			
			groups = new MGroup[items.length()];
		
			for (int i=0; i< items.length(); i++) {
				JSONObject collection = items.getJSONObject(i);
				
				groups[i] = new MGroup(con);
				
				groups[i].id   = collection.getString("id");
				groups[i].name = collection.getString("name");
				groups[i].type = collection.getString("type");
				groups[i].size = collection.getInt("size");
			}
		} catch (Exception e) {
			Log.e("MendeleyConnector","Got exception when parsing online group data");
			Log.e("MendeleyConnector",e.getClass().getSimpleName() + ": " + e.getMessage());
		}
		return groups;
	}
	
	public String [] getGroupDocuments(MGroup group) {
		return getGroupDocuments(group,0);
	}
	
	public String [] getGroupDocuments(MGroup group, int page) {
		if (! isConnected()) {
			StartActivity.singleton.doAuthentication(StartActivity.MENDELEY_RETRIEVE_COLLECTION);
			return null;
		}
		
		String [] ids = null;
		try {
			String strResponse = connect("http://www.mendeley.com/oapi/library/groups/"+group.id+"/?page="+page+"&consumer_key=" + m_consumerkey);

			Log.i("MendeleyComm", "Collection: " + group.id + "\n" +strResponse);
			
			JSONObject colData = new JSONObject(strResponse);

			JSONArray documents = colData.getJSONArray("document_ids");
			ids = new String[documents.length()];
			for (int i=0; i< documents.length(); i++) {
				ids[i] = documents.getString(i);
			}
			
			// make sure we get all pages
			int totalpages = colData.getInt("total_pages");
			if (page+1 < totalpages ) {
				String [] adddocs = getGroupDocuments(group, page+1);
				
				String [] alldocs = new String[ids.length + adddocs.length];
				for (int i=0;i<ids.length;i++) alldocs[i] = ids[i];
				for (int i=0;i<adddocs.length;i++) alldocs[ids.length+i] = adddocs[i];
				ids = alldocs;
			}
			
		} catch (Exception e) {
			Log.e("MendeleyConnector","Got a " + e.getClass().getName() + ": " + e.getMessage());
		}
		
		return ids;
	}
	
	public MDocument getDocument(MDocument doc) {
		
		if (! isConnected()) {
			StartActivity.singleton.doAuthentication(StartActivity.MENDELEY_RETRIEVE_DOCUMENT);
			return null;
		}
		
		MDocument mdoc = null;
		try {
			String strResponse = connect("http://www.mendeley.com/oapi/library/documents/"+doc.m_docid+"/?consumer_key=" + m_consumerkey);
			Log.i("MendeleyComm", "Document: " + doc.m_docid + "\n" +strResponse);
			
			mdoc = parseDocumentResult(doc, strResponse);

		} catch (Exception e) {
			Log.e("MendeleyConnector","While in getDocument(): Got a " + e.getClass().getName() + ": " + e.getMessage());
		}
		
		return mdoc;
	}
	
	public MDocument getGroupDocument(MGroup grp, MDocument doc) {

		if (! isConnected()) {
			StartActivity.singleton.doAuthentication(StartActivity.MENDELEY_RETRIEVE_DOCUMENT);
			return null;
		}

		String docid = doc.m_docid.substring(doc.m_docid.lastIndexOf("/")+1); 
		String group = doc.m_docid.substring(0,doc.m_docid.indexOf("/"));
		
		MDocument mdoc = null;
		try {
			String strResponse = connect("http://www.mendeley.com/oapi/library/groups/"+group+"/"+docid+"/?consumer_key=" + m_consumerkey);
			Log.i("MendeleyComm", "Document: " + docid + "\n" +strResponse);
			mdoc = parseDocumentResult(doc,strResponse); 
		} catch (Exception e) {
			Log.e("MendeleyConnector","While in getDocument(): Got a " + e.getClass().getName() + ": " + e.getMessage());
		}
		
		return mdoc;
	}
	
	private MDocument parseDocumentResult(MDocument mdoc, String strResponse) throws JSONException {
		
		JSONObject doc = new JSONObject(strResponse);
		
		try{ mdoc.title  = doc.getString("title"); } catch(JSONException e) {Log.w("WebConnector","Parsing error while getting document title");}
		try{ mdoc.year   = doc.getString("year");  } catch(JSONException e) {Log.w("WebConnector","Parsing error while getting document year");}
		try{ mdoc.notes  = doc.getString("notes"); } catch(JSONException e) {Log.w("WebConnector","Parsing error while getting document notes");}
		try{ mdoc.type   = doc.getString("type");  } catch(JSONException e) {Log.w("WebConnector","Parsing error while getting document type");}
		try{ mdoc.urls   = new String[] {doc.getString("url")};   } catch(JSONException e) {Log.w("WebConnector","Parsing error while getting document url");}
		try{ mdoc.pages  = doc.getString("pages"); } catch(JSONException e) {Log.w("WebConnector","Parsing error while getting document pages");}
		try{ mdoc.docabstract=doc.getString("abstract"); } catch(JSONException e) {Log.w("WebConnector","Parsing error while getting document abstract");}
		
		try{ 
			JSONArray authors = doc.getJSONArray("authors");
			String [] strAuthors = new String[authors.length()];
			for (int j=0; j<authors.length();j++) {
				strAuthors[j] = authors.getString(j);
			}
			mdoc.authors = strAuthors;
		} catch(JSONException e) {Log.w("WebConnector","Parsing error while getting document authors");}
		
		try{ 
		
			JSONArray tags = doc.getJSONArray("tags");
			String [] mtags = new String[tags.length()];
			for (int j=0; j<tags.length();j++) {
				mtags[j] = tags.getString(j);
			}
			mdoc.tags = mtags;
		} catch(JSONException e) {Log.w("WebConnector","Parsing error while getting document tags");}

		
		try{ 
			JSONObject ids = doc.getJSONObject("identifiers");
			mdoc.identifiers = new HashMap<String,String>();
			JSONArray names = ids.names();
			for (int j=0; j<names.length(); j++) {
				mdoc.identifiers.put(names.getString(j), ids.getString(names.getString(j)));
			}
		} catch(JSONException e) {Log.w("WebConnector","Parsing error while getting document identifiers");}

		try{ 
			JSONObject ids = doc.getJSONObject("discipline");
			mdoc.discipline = new HashMap<String,String>();
			JSONArray names = ids.names();
			for (int j=0; j<names.length(); j++) {
				mdoc.discipline.put(names.getString(j), ids.getString(names.getString(j)));
			}
		} catch(JSONException e) {Log.w("WebConnector","Parsing error while getting document disciplines");}
		
		return mdoc;
	}
	
	
	// ////////////////////////////////////////////////////////////////////////
	// Online access

	private static final int STATE_OFFLINE    = 0;
	private static final int STATE_CONNECTING = 1;
	private static final int STATE_CONNECTED = 1;
	private int m_state = STATE_OFFLINE;
	
	// this code imports the OAuth keys from a dummy class with just two static strings
	private String m_consumerkey = OAuthKeys.CONSUMERKEY;
	private String m_consumersecret = OAuthKeys.CONSUMERSECRET;
	
	private String m_authURL;
	
    private OAuthConsumer m_consumer = new DefaultOAuthConsumer(
            m_consumerkey,m_consumersecret,
            SignatureMethod.HMAC_SHA1);

    private OAuthProvider m_provider;
    
	private String connect(String strURL) throws Exception{
		URL url = new URL(strURL);
		HttpURLConnection request = (HttpURLConnection) url.openConnection();
		m_consumer.sign(request);
		
		request.connect();

		if (request.getResponseCode() == 200) {
			
			String strResponse = "";
			InputStreamReader in = new InputStreamReader((InputStream)request.getContent());
			BufferedReader buff = new BufferedReader(in);
			
			for (String strLine = ""; strLine != null; strLine = buff.readLine()) {
				strResponse += strLine + "\n";
			}
			in.close();
			
			return strResponse;
		} else {
			throw new Exception("Mendeley Server returned " + request.getResponseCode() + ": " + request.getResponseMessage());
		}
			
	}

}
